const RpcMgrError = Object.freeze({
    OK: 'OK',
    TIME_OUT: 'TIME_OUT',
    SN_REPEAT: 'SN_REPEAT'
})

class RpcMgr {
    constructor() {
        this.allMsgID = new Set();//所有协议ID
        this.timeOutMs = 30000;//超时时间（毫秒）
        this._cbList = [];//回调队列
        this._cbMap = new Map();
        this._snSeed = 1;
    }

    isRegistered(cbMsgId) {
        return this.allMsgID.has(cbMsgId);
    }

    registerCallback(sn, doFunc, cbMsgId=0, nowMs=0) {
        if (nowMs == 0) {
            nowMs = Date.now();
        }
        let cbFunc = {
            timeOut: parseInt(nowMs+this.timeOutMs),
            sn: sn,
            func: doFunc,
        }
        let oldFunc = this._cbMap.get(sn);
        if (oldFunc) {
            oldFunc.func(RpcMgrError.SN_REPEAT, 0, 0, 0);
        }
        this._cbMap.set(sn, cbFunc);
        this._cbList.push({ timeOut: cbFunc.timeOut, sn: sn});
        if (cbMsgId > 0) {
            this.allMsgID.add(cbMsgId);
        }
    }

    autoRegister(doFunc, cbMsgId=0, nowMs=0) {
        const sn = this._snSeed++;
        if (this._snSeed > 100000000) {
            this._snSeed = 1;
        }
        this.registerCallback(sn, doFunc, cbMsgId, nowMs);
        return sn;
    }

    update() {
        const nowMs = Date.now();
        for (let i = 0; i < 100; i++) {
            if (this._cbList.length > 0) {
                const top = this._cbList[0];
                if (nowMs > top.timeOut) {
                    const cbFunc = this._cbMap.get(top.sn);
                    if (cbFunc) {
                        if (cbFunc.timeOut == top.timeOut) {
                            cbFunc.func(RpcMgrError.TIME_OUT, 0, 0, 0);//超时回调
                            this._cbMap.delete(top.sn);
                        }
                    }
                    this._cbList.shift();
                } else {
                    break;
                }
            } else {
                break;
            }
        }
    }

    updateInterval() {
        setInterval(() => {
            this.update();
        }, 1000);
    }

    triggerCallback(sn, socket, data) {
        const cbFunc = this._cbMap.get(sn);
        if (cbFunc) {
            cbFunc.func(RpcMgrError.OK, socket, data);
            this._cbMap.delete(sn);
        } else {
            console.log('rpc callback sn not found:'+sn);
        }
    }
}

module.exports = RpcMgr;
